home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 266_01 / update.txt < prev    next >
Text File  |  1989-04-19  |  12KB  |  287 lines

  1. **** This text is extracted from the volume 7.4. ****
  2.  
  3. Porting Microplox To XENIX
  4.  
  5. Microplox (CUG266) is a graph drawing program that works under CP/M 
  6. or MS-DOS using an Epson FX-80 printer (or in our case, a laser printer 
  7. which can emulate the Epson). Recently we adapted the MS-DOS original 
  8. to work under XENIX. In a technical sense the incompatibilities were 
  9. relatively mild and commonplace (BIOS I/O calls had to be mapped to 
  10. XENIX I/O capabilities and int/pointer puns had to be eliminated), 
  11. but locating and understanding these problems was somewhat complicated 
  12. by Microplox's unusual coding and design style.
  13.  
  14. The Implementation
  15.  
  16. Microplox consists of two programs:plox translates a graph 
  17. specification into a file of reasonably abstract plotting commands 
  18. (PLOXCOM.DAT) and ploteps builds and prints an Epson 
  19. compatible bit map from the plotting commands.
  20.  
  21. ploteps is conventional in design and coding, but plox 
  22. is (at least at the top level) an unusual collection of object-like 
  23. functions, one for each different type of statement in the Microplox 
  24. specification grammar. These object-like functions have uniform message-passing 
  25. interfaces which require a single text string (messages) as input.  Each 
  26. message consists of a series of keyword-value pairs.  The function 
  27. parses the keyword to select an appropriate method for processing 
  28. the associated value.  Thus a specification file can be parsed merely 
  29. by repackaging each statement as a message and sending it to the appropriate 
  30. object function. 
  31.  
  32. Each of these object functions also owns certain private (static) 
  33. data.  Low level messages (those with strictly local effect) result 
  34. either in changes to this private data or immediate output of a plotting 
  35. command or both.  Higher level messages (those that affect non-local 
  36. data), however, may require changes to the private data of several 
  37. distinct object functions.  Object functions effect changes in the 
  38. private data of other object functions by constructing a message and 
  39. sending it via the function SendSpec().
  40.  
  41. SendSpec() requires three arguments: a string representing 
  42. the method part of the message, a value, and a pointer to the target 
  43. object function. SendSpec() formats the value as a string, 
  44. appends it to the method and invokes (via the pointer) the target 
  45. function with the resulting message as the single input parameter 
  46. (see Listing 1). 
  47.  
  48. /* Send a "keyword value" string to a given function */
  49.  
  50. void SendSpec (KeyWord, Value, Process)
  51. char *KeyWord;
  52. int Value, (*Process)();
  53. {
  54. char Digits[7], Spec[20];
  55.  
  56. strcpy (Spec, KeyWord);
  57. strcat (Spec, " ");
  58. sprintf (Digits, "%d", Value);
  59. strcat (Spec, Digits);
  60. (*Process) (Spec);
  61. }
  62.  
  63. (Listing 1)
  64.  
  65. The second paramter, Value, is declared as a simple int.  Unfortunately, 
  66. not all methods require a simple int as their value.  Some 
  67. require strings, others need several values.  In classic C tradition, 
  68. SendSpec() ignores these differences, assuming that int 
  69. is actually a universal type.  Both pointers and ints are passed 
  70. as values.  SendSpec() converts each to an ASCII string, 
  71. as if an int had been received, and the receiving function 
  72. must examine the method and use the received value correctly. 
  73.  
  74. This type pun works fine as long as ints and pointers 
  75. are the same size and pointers can be reliably converted to and from 
  76. an integer representation. But, when pointers are 32 bits and ints 
  77. 16 (as in our target XENIX environment), the result is a program that 
  78. compiles without warning but immediately dumps core when executed.
  79.  
  80. We considered several alternative repairs.  We could merely have lengthened 
  81. Value to 32 bits (cast ints to long at each call).  With 
  82. appropriate typedefs and a few coding changes, we could have 
  83. made it easy for others to readjust the variable sizes to match other 
  84. machines.  But what about environments where pointers don't have a 
  85. meaningful or unique integer representation? We decided there was 
  86. no certain way to sprintf() a pointer to a character string 
  87. and still be able to convert it back.  
  88.  
  89. We also considered passing through a union, but that seemed to complicate 
  90. both the sending and receiving code at least as much as the obvious 
  91. alternative: adding a second field to the basic message.
  92.  
  93. The New Message
  94.  
  95. As originally coded, all plox messages were a single string, 
  96. possibly with multiple method pairs. We modified that so that messages 
  97. were a string and a possibly NULL pointer value (see Listing 
  98. 2). Most methods are still paired with their values in the message 
  99. string, but methods requiring a pointer value retreive it from the 
  100. pointer parameter instead.  
  101.  
  102. void SendSpec (KeyWord, Value, Scaleptr, Process)
  103. char *KeyWord;
  104. int Value, (*Process)();
  105. SCALING *Scalepr;
  106. {
  107. char Digits[7], Spec[20];
  108.  
  109. strcpy (Spec, KeyWord);
  110. strcat (Spec, " ");
  111. if (Value != 0) {
  112.   sprintf (Digits, "%d", Value);
  113.   strcat (Spec, Digits);
  114. }
  115. (*Process) (Spec, Scaleptr);
  116. }
  117.  
  118. (Listing 2)
  119.  
  120. We considered putting both parts of the message in a structure, but 
  121. that approach would have required significant changes throughout the 
  122. program in how variables are declared and used.  Adding a parameter 
  123. required changes only to the calls to SendSpec(),the calling 
  124. interfaces of the object functions, and the few methods which expect 
  125. pointer values.  Passing what is conceptually a single message as 
  126. two distinct parameters lacks elegance, but works and seems to do 
  127. less injury to the code than the alternatives. 
  128.  
  129. The Printer Driver
  130.  
  131. ploteps is responsible for creating a graphic image and sending 
  132. it to the printer. In the orginal program, the image was output via 
  133. calls to BDOS functions a single-user approach not feasible 
  134. under XENIX. We considered two alternatives: replacing the low-level 
  135. MS-DOS I/O calls with comparable low-level XENIX/UNIX I/O calls, and 
  136. restructuring the I/O to take advantage of UNIX streams through high-level 
  137. buffered I/O commands. 
  138.  
  139. The first method is straightforward. The program opens the printer 
  140. device file and writes the graphic image directly to the printer. 
  141. System functions open(), read(), write(), ioctl() and close() 
  142. will have to be used in the program. The second method is much more 
  143. flexible, easier to debug, and doesn't interfere with sharing the 
  144. printer among several users. Under the second method, the program 
  145. generates a binary temporary file that holds a graphic image. This 
  146. binary file is sent to the print spooler using the lp or lpr 
  147. commands (Figure 1).
  148.  
  149.  -------                       ---------                  ----
  150. | plox  | --> PLOTCOM.DAT --> | ploteps | --> IMAGE ---> | lp | 
  151.  -------                       ---------                  ----
  152.                                                            |
  153.                                                            V
  154.                                                           ---------------------
  155.                                                          | printer    | device |
  156. (Figure 1)                                               | interface  | driver |
  157.                                                           ---------------------
  158.  
  159. Unfortunately the stock printer interface performs certain post-processing 
  160. on text files. At a minimum newlines (linefeeds) are converted to 
  161. CR-LF pairs. The binary file includes escape sequences to put the 
  162. printer in graphics mode so that it will interpret a binary 0x0A 
  163. as a dot pattern, but the interface and printer device driver don't 
  164. recognize the graphics mode, and continue to expand linefeeds. 
  165. To circumvent this problem, we modified the interface program (a piece 
  166. of shell script) by adding a local binary option.
  167.  
  168. Patching The Interface
  169.  
  170. Each printer device under UNIX/XENIX has its own printer interface 
  171. program at the subdirectory, /usr/spool/lp/interface. The printer 
  172. interface program is a piece of shell script that is invoked each 
  173. time lp is executed with the target printer. The task of the 
  174. interface program is to configure the device driver (via stty) 
  175. to properly handle the printer, displa